package pt.compflow.activity.workflow;

import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pt.compflow.activity.gateway.GatewayExecutor;
import pt.compflow.flowontology.model.Activity;
import pt.compflow.flowontology.model.Event;
import pt.compflow.flowontology.model.EventInterface;
import pt.compflow.flowontology.model.FLOWO;
import pt.compflow.flowontology.model.FlowOntologyModel;
import pt.compflow.flowontology.model.Gateway;
import pt.compflow.flowontology.model.Workflow;
import pt.compflow.interfaces.IOInterface;
import pt.compflow.interfaces.event.EventSubscription;
import pt.compflow.storage.TripleStoreConnection;
import pt.compflow.tools.OntModelHelper;

import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;

/**
 *	This command runs an activity according to its type (Workflow, Task, Event or Gateway)
 *
 */
public class WorkflowControllerRunActivityCommand implements Runnable {

	private static final Logger LOG = LoggerFactory.getLogger(WorkflowControllerRunActivityCommand.class);

	protected WorkflowController controller;
	protected String jobUri;
	protected String activityUri;
	
	public WorkflowControllerRunActivityCommand(WorkflowController controller, String jobUri, String activityUri) {
		this.controller = controller;
		this.activityUri = activityUri;
		this.jobUri = jobUri;
	}

	@Override
	public void run() {
		
		TripleStoreConnection store = controller.getContext().getStoreConnection();
		store.openStore();
				
		try {
			FlowOntologyModel jobModel = store.connectNamedModel(jobUri);
			Activity act = jobModel.getActivityWithURI(activityUri);
						
			if(act.getPropertyResourceValue(FLOWO.hasState).getURI().compareTo(FLOWO.notStarted.getURI()) != 0) {
				LOG.warn("Cannot start activity with invalid state; state different from 'notStarted'");
				store.closeStore();
				return;
			}
			
			// find workflow instance (needed to access the workflow ontology and generate assignment uris)
			Workflow wf = act.getActivityOfWorkflow();
					
			OntModelHelper ontModelHelper = controller.getContext().getOntModelHelperManager().get(store, jobUri, wf.getRootWorkflow().getLocalName());
						
			if(act.isGateway()) {
				Resource actClass = ontModelHelper.getDirectLeafType(act, FLOWO.Gateway.getURI());
				GatewayExecutor gatewayExecutor = controller.getContext().getGatewayExecutorManager().get(ontModelHelper.getOntModel(), actClass);
				
				gatewayExecutor.setup(ontModelHelper, jobModel, act.as(Gateway.class));
				if(gatewayExecutor.isReady()) {
					controller.onActivityStarted(jobUri, activityUri);
					gatewayExecutor.run();
					controller.onActivityFinished(jobUri, act.getURI(), gatewayExecutor.getNextActivities());
				}
			}
			else if(act.isEvent()) {
				FlowOntologyModel engineModel = store.connectModel();
				Iterator<Statement> evInterfaceIt = act.as(Event.class).listProperties(FLOWO.deliveredThrough).toList().iterator();
				Resource actClass = ontModelHelper.getDirectLeafType(act, FLOWO.Event.getURI());
				
				while(evInterfaceIt.hasNext()) {
					EventInterface evInterface = engineModel.getEventInterfaceWithURI(evInterfaceIt.next().getObject().asResource().getURI());
					
					Resource evInterfaceClass = ontModelHelper.getDirectLeafType(evInterface, FLOWO.EventInterface.getURI());
					IOInterface interfaze = controller.getContext().getEventInterfaceManager().get(evInterface, evInterfaceClass, ontModelHelper.getOntModel(), controller.getContext(), store);
					String subscriptionId = ((pt.compflow.interfaces.event.EventInterface)interfaze).subscribe(controller.getContext(), store, ontModelHelper, jobModel, engineModel, jobUri, actClass, act.as(Event.class));
					act.addLiteral(jobModel.getProperty(EventSubscription.SUBSCRIPTION_PROP), subscriptionId);
				}
				
				controller.onActivityStarted(jobUri, activityUri);
			}
			else if(act.isTask()){
				controller.getContext().getTaskController().startTask(jobUri, activityUri);
			}
			else if(act.isWorkflow()){
				controller.startWorkflow(jobUri, activityUri);
			} else {
				//if this happens, then the code needs another entry here...
				//if this happens, the subclass relationship in the ontology is probably wrong/invalid
				// TODO add general code to Activity class (a runnable?)
				throw new RuntimeException("Invalid activity type in ontology; type not found!");
			}	
		
		} catch(Throwable e) {
			LOG.error("Unable to run Activity " + activityUri, e);
			controller.onActivityError(jobUri, activityUri);
		}
		
		store.closeStore();
	}
	
}
